
#include <curses.h>
#include "drawerADT.h"
#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>

#define INFO_WIDTH 120

struct drawerCDT{
	int blockSizeX;
	int blockSizeY;
	int numBlocksX;
	int numBlocksY;
	int infoLinesCurrentPos[MAX_INFO_LINES];
	int cityColorPair;
	pthread_mutex_t mutex;
	WINDOW* win;
	WINDOW* infoWin;
} drawerCDT;

void doCity(drawerADT drawer);
void startColors(drawerADT drawer);
char getCharFromDirection(DIRECTION direction);
int getOriginalChar(drawerADT drawer,int posX, int posY);
void wrapPositions(drawerADT drawer,int* x, int* y);
void drawCharAtPos(drawerADT drawer,COLOR color, int posX, int posY, char c);
char getCharFromOrientation(ORIENTATION orientation);

void
freeDrawer(drawerADT drawer)
{

	if(drawer==NULL)
		return;
	if(drawer->win != NULL)
		delwin(drawer->win);
	endwin();
	free(drawer);
	return;
}

drawerADT 
newDrawer(int blockSizeX, int blockSizeY, int numBlocksX, int numBlocksY)
{
	drawerADT drawer;
	int i;

	if((drawer = malloc(sizeof(struct drawerCDT))) == NULL)
	{
		return NULL;
	}
	
	initscr();
	curs_set(0);
	//set  window
	drawer->win = newwin((blockSizeY + 2) * numBlocksY, (blockSizeX + 2 ) * numBlocksX, 0, 0);
	if(drawer->win == NULL)
	{
		free(drawer);
		return NULL;
	}

	drawer->infoWin = newwin(MAX_INFO_LINES, INFO_WIDTH,(blockSizeY + 2) * numBlocksY + 1,  0);
	if(drawer->infoWin == NULL)
	{
		delwin(drawer->win);
		endwin();
		free(drawer);
		return NULL;
	}

	drawer->blockSizeX = blockSizeX;
	drawer->blockSizeY = blockSizeY;
	drawer->numBlocksX = numBlocksX;
	drawer->numBlocksY = numBlocksY;
	drawer->cityColorPair = 0;
	pthread_mutex_init(&drawer->mutex, NULL);

	for(i=0;i<MAX_INFO_LINES;i++)
		drawer->infoLinesCurrentPos[0] = 0;

	startColors(drawer);
	
	
	
	doCity(drawer);

	return drawer;
}

void
startColors(drawerADT drawer)
{
	start_color();
	init_pair(1,0,COLOR_WHITE);
	init_pair(WHITE,COLOR_WHITE,0);
	init_pair(YELLOW,COLOR_YELLOW,0);
	init_pair(CYAN,COLOR_CYAN,0);
	init_pair(MAGENTA,COLOR_MAGENTA,0);
	init_pair(RED,COLOR_RED,0);
	init_pair(GREEN,COLOR_GREEN,0);
	init_pair(BLUE,COLOR_BLUE,0);
	
	return;
}

void
doCity(drawerADT drawer)
{
	int numPaintings = (drawer->blockSizeY +2) * (drawer->blockSizeX +2) * drawer->numBlocksX * drawer->numBlocksY;
	int i, rowCheck, columnCheck;
	
	pthread_mutex_lock(&drawer->mutex);
	
	for(i=0;i<numPaintings;i++)
	{
		rowCheck = (i/((drawer->blockSizeX+2) * drawer->numBlocksX))%(drawer->blockSizeY +2);
		columnCheck = i%(drawer->blockSizeX+2);
		if(rowCheck == 0 || rowCheck == 1)
		{
			if(columnCheck == 0 || columnCheck == 1)
				waddch(drawer->win, ' ' /*+*/ );
			else
				waddch(drawer->win, ' ' /*-*/);
		}
		else
		{
			if(columnCheck == 0 || columnCheck == 1)
				waddch(drawer->win, ' '/*|*/ );
			else
				waddch(drawer->win, ' '/*#*/ | COLOR_PAIR(1));
		}
	}
	pthread_mutex_unlock(&drawer->mutex);
}

void
printInfo(drawerADT drawer, int line, char* info)
{
	int i;
	pthread_mutex_lock(&drawer->mutex);
	wmove(drawer->infoWin, line, 0);

	for(i=0; i<INFO_WIDTH && info[i] != '\0';i++)
	{
		waddch(drawer->infoWin, info[i]);
	}
	while(i < INFO_WIDTH)
	{
		i++;
		waddch(drawer->infoWin, ' ');
	}

	pthread_mutex_unlock(&drawer->mutex);
}

void
eraseInfoLine(drawerADT drawer, int line)
{
	int i;
	
	pthread_mutex_lock(&drawer->mutex);
	wmove(drawer->infoWin, line, 0);
	for(i=0; i< drawer->infoLinesCurrentPos[line]; i++)
	{
		waddch(drawer->infoWin, ' ');
	}
	

	drawer->infoLinesCurrentPos[line] = 0;
	pthread_mutex_unlock(&drawer->mutex);
	return;
}

void 
setSemaphore(drawerADT drawer,ORIENTATION orientation, COLOR color, int posX, int posY)
{
	char orientedSemaphore = getCharFromOrientation(orientation);
	wrapPositions(drawer, &posX, &posY);
	drawCharAtPos(drawer, color, posX, posY, orientedSemaphore);
}

char
getCharFromOrientation(ORIENTATION orientation)
{
	if(orientation == VER)
		return '|';
	else if(orientation == HOR)
		return '-';
	else
		return ' ';
}

void
addCar(drawerADT drawer,DIRECTION direction, COLOR color, int posX, int posY)
{	
	char dir = getCharFromDirection(direction);
	
	wrapPositions(drawer, &posX, &posY);
	
	drawCharAtPos(drawer, color, posX, posY, dir);
	
}

void
wrapPositions(drawerADT drawer,int* x, int* y)
{
	
	int numRows = (drawer->blockSizeY+2) * drawer->numBlocksY;
	int numCols = (drawer->blockSizeX+2) * drawer->numBlocksX;
	
	//do convertions for negative numbers
	if(*y<0)
	{
		*y = -*y;
		*y = numRows - (*y % numRows);
	}
	if(*x<0)
	{
		*x = -*x;
		*x = (drawer->blockSizeX+2) * drawer->numBlocksX - (*x % numCols);
	}
	
	//wrap around the images
	*y = *y % ((drawer->blockSizeY+2) * drawer->numBlocksY);
	*x = *x % ((drawer->blockSizeX+2) * drawer->numBlocksX);

}

void
drawCharAtPos(drawerADT drawer,COLOR color, int posX, int posY, char c)
{
	//mutex aca
	pthread_mutex_lock(&drawer->mutex);
	mvwaddch(drawer->win, posY, posX, c | COLOR_PAIR(color));
	pthread_mutex_unlock(&drawer->mutex); 

}

int
removeObject(drawerADT drawer,int posX, int posY)
{
	
	wrapPositions(drawer, &posX, &posY);
	
	
	drawCharAtPos(drawer, drawer->cityColorPair, posX, posY, ' ');

	return 0;
}

int
getOriginalChar(drawerADT drawer,int posX, int posY)
{
	int xIsInBlockHeight, yIsInBlockHeight;
	
	yIsInBlockHeight = !(posY % (drawer->blockSizeY+2) == 0 || posY % (drawer->blockSizeY+2) == 1);
	xIsInBlockHeight = !(posX % (drawer->blockSizeX+2) == 0 || posX % (drawer->blockSizeX+2) == 1);
	
	if(yIsInBlockHeight)
	{
		if(xIsInBlockHeight)
			return ' '/*#*/ ;
		else
			return ' '/*|*/;
	}
	else
	{
		if(xIsInBlockHeight)
			return ' '/*-*/;
		else
			return ' '/*+*/;
	}

}


char
getCharFromDirection(DIRECTION direction)
{
	switch(direction)
	{
		case N:
			return '^';
		case S:
			return 'v';
		case W:
			return '<';
		case E:
			return '>';
		default:
			return '*';
	}
}


void
draw(drawerADT drawer)
{
	pthread_mutex_lock(&drawer->mutex);
	wrefresh(drawer->win);
	pthread_mutex_unlock(&drawer->mutex);
	return;
}

void
drawInfo(drawerADT drawer)
{
	pthread_mutex_lock(&drawer->mutex);
	wrefresh(drawer->infoWin);
	pthread_mutex_unlock(&drawer->mutex);
	return;
}



